/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package nl.hanze.bbs.session;

import java.util.*;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import nl.hanze.bbs.entity.Boete;

/**
 *
 * @author Herman
 */
@Stateless
public class BbsSB implements BbsSBLocal {

    // Add business logic below. (Right-click in editor and choose
    // "Insert Code > Add Business Method")
    @PersistenceContext(unitName = "Wk3Opg3PU")
    private EntityManager EM;
    
    private static final int MAX_ZWAARTE = 5;
    
    /**
     * Bereken wat de hoogte van de boete moet zijn aan de hand van het boete
     * verleden van de overtreder middels zijn/haar BSN en de overtreding
     * 
     * @param bsn               BSN nummer overtreder als String
     * @param snelheidsLimiet   De plaatselijke snelheidslimiet als int
     * @param geredenSnelheid   De gemeten snelheid als int
     * @return hoogteBoete      De bepaalde hoogte van de boete als int
     */
    @Override
    public int berekenBoete(String bsn, int snelheidsLimiet, int geredenSnelheid) {
        // Eerst bepalen we de initiele boete en de zwaarte punten
        int boete = initieleBoete(snelheidsLimiet, geredenSnelheid);
        int zwaarte = zwaartePunt(snelheidsLimiet, geredenSnelheid);
        // Dan passen we 2 regels toe. Regel 1 is dat boetes in zones waar
        // niet harder dan 50 mag worden gereden worden verdubbeld:
        if (snelheidsLimiet <= 50)
            boete *= 2;
        // Regel 2 is dat aan de hand van het aantal verzamelde zwaartepunten
        // het afgelopen jaar de boete nog verder verzwaard wordt.
        boete += berekenZwaarte(bsn, zwaarte) * .5 * boete;
        return boete;
    }
    
    private int initieleBoete(int limiet, int snelheid) {
        // Voor de initiele boete wordt gekeken naar hoeveel harder er werd
        // gereden dan toegestaan.
        int tesnel = snelheid - limiet;
        if (tesnel < 1)  return 0;
        if (tesnel < 5)  return 25;
        if (tesnel < 10) return 50;
        if (tesnel < 15) return 75;
        if (tesnel < 20) return 100;
        if (tesnel < 30) return 150;
        if (tesnel < 40) return 250;
        if (tesnel < 50) return 400;
        return 0; // rijbewijs ingevorderd
    }
    
    private int zwaartePunt(int limiet, int snelheid) {
        // Het zwaartepunt voor deze overtreding wordt bepaald aan de hand van
        // hoe veel harder er werd gereden dan toegestaan.
        int tesnel = snelheid - limiet;
        if (tesnel < 1)  return 0;
        if (tesnel < 10) return 1;
        if (tesnel < 20) return 2;
        if (tesnel < 30) return 3;
        if (tesnel < 40) return 4;
        if (tesnel < 50) return 5;
        return 0; // rijbewijs ingevorderd
    }

    private int berekenZwaarte(String bsn, int zwaarte) {
        // Bij de zwaartepunten van deze overtreding worden zwaartepunten van
        // voorgaande boetes opgeteld.
        zwaarte += countZwaarte(getBoetes(bsn));
        // vervolgens wordt bepaald hoevaak de boete met 50% verhoogd moet
        zwaarte -= MAX_ZWAARTE;
        if (zwaarte < 1)
            return 0;
        return zwaarte;
    }
    
    private List<Boete> getBoetes(String bsn) {
        // Selecteer de boetes behorend bij dit BSN en minder dan een jaar oud
        Date jaarGeleden = new Date();
        jaarGeleden.setYear(jaarGeleden.getYear()-1);
        TypedQuery<Boete> tq = 
                EM.createQuery("SELECT b FROM Boete b WHERE b.bsn = :bsn "
                             + "AND b.datum >= :jaarGeleden", Boete.class);
        tq.setParameter("bsn", bsn);
        tq.setParameter("jaarGeleden", jaarGeleden);
        return tq.getResultList();
    }
    
    private int countZwaarte(List<Boete> boetes) {
        // Itereer door de opgehaalde boetes heen en tel de zwaartepunten op
        int zwaarte = 0;
        Iterator<Boete> it = boetes.iterator();
        while (it.hasNext()) {
            Boete b = it.next();
            zwaarte += zwaartePunt(b.getSnelheidslimiet(), b.getGereden());
        }
        return zwaarte;
    }
}
